package com.demo.client.handler;

import com.demo.client.NettyClient;
import com.demo.client.config.ClientCfg;
import com.demo.client.retry.DefaultRetryPolicy;
import com.demo.client.retry.RetryPolicy;
import com.demo.common.protocal.MyMessage;
import com.demo.common.protocal.OpType;
import io.netty.channel.*;
import io.netty.util.concurrent.ScheduledFuture;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.TimeUnit;

@Slf4j
@ChannelHandler.Sharable
public class ConnectHandler extends SimpleChannelInboundHandler<MyMessage> {

    private int retries = 0;

    private RetryPolicy retryPolicy;

    private NettyClient nettyClient;

    public ConnectHandler(NettyClient nettyClient) {
        this.nettyClient = nettyClient;
        this.retryPolicy = new DefaultRetryPolicy();
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        log.debug("========================================================================");
        log.debug("| [SUCCESS]     Server has been connected                              |");
        log.debug("========================================================================");
        retries = 0;
        ctx.fireChannelActive();
        super.channelActive(ctx);
        sendPing(ctx.channel());
    }

    private void sendPing(Channel channel) {

        ScheduledFuture<?> future = channel.eventLoop().schedule(new Runnable() {
            @Override
            public void run() {
                channel.writeAndFlush(MyMessage.PingMessage())
                        .addListener(f -> {
                           log.debug("发送心跳包" + (f.isSuccess() ? "成功" : "失败"));
                            if (f.isSuccess()) {
                                sendPing(channel);
                            }
                        });
                    }
        }, ClientCfg.HEART_INTERVAL, TimeUnit.SECONDS);
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        if (retries == 0) {
            log.debug("========================================================================");
            log.debug("|  [FAILURE]   Server has been disconnected ...                        |");
            log.debug("========================================================================");
            ctx.disconnect();
            ctx.close();
        }

        boolean allowRetry = retryPolicy.willRetry(++retries);
        if (allowRetry) {

            long sleepSecond = retryPolicy.retryInterval(retries);
            log.debug("Try connecting to the server for {} time in {} s", retries, sleepSecond);

            final EventLoop eventLoop = ctx.channel().eventLoop();
            eventLoop.schedule(() -> {
                nettyClient.connect();
            }, sleepSecond, TimeUnit.SECONDS);
            ctx.fireChannelInactive();
        } else {
            log.debug("Unable to connect after {} attempts, client will close soon ...", retries - 1);
            nettyClient.shutdown();
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        // 当Channel已经断开的情况下, 仍然发送数据, 会抛异常, 该方法会被调用.
        cause.printStackTrace();
        ctx.close();
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, MyMessage msg) throws Exception {
        if (OpType.PONG == msg.getType()) {
            log.debug("服务器回复： 消息类型：{}, 消息长度：{}, 消息体：{}", msg.getType(), msg.getContentLength(), msg.getContent());
        }
    }
}
